/*
Copyright 2014 Yury Kharlamov

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Generated Inversion-Of-Control container
https://code.google.com/p/gen-ioc/

* * *

Event Dispatcher implementation
*/

#include "stdafx.h"
#include <queue>
#include "GenIOC/EventDispatcher.h"
#include "GenIOC/TypeInfoWrapper.h"

template<>
IfaceId GetTypeId<IEventDispatcher>(){ return "IEventDispatcher"; }

class EventDispatcher : public IEventDispatcher
{
	struct DispatchEntry
	{
		DispatchEntry(const boost::any& evt, const SubscrList& subscribers)
			:evt(evt), subscribers(subscribers){}
		boost::any evt;
		SubscrList subscribers;
	};
public:
	EventDispatcher()
		:m_dispatchThread(&EventDispatcher::DispatchThread, this)
	{
		m_destroyed = std::make_shared<volatile bool>();
		*m_destroyed = false;
		//printf("EventDispather\n");
	}
	~EventDispatcher()
	{
		/*printf("~EventDispather, id: %i, other: %i\n"
			, boost::this_thread::get_id()
			, m_dispatchThread.get_id());*/
		*m_destroyed = true;
		if (m_dispatchThread.get_id() != boost::this_thread::get_id())
		{
			m_dispatchThread.interrupt();
			m_dispatchThread.join();
		}
	}
	void Dispatch(const boost::any& evt, const SubscrList& subscribers) override
	{
		boost::lock_guard<boost::mutex> lock(m_mutex);
		m_dispatches.push(DispatchEntry(evt, subscribers));
		m_cond.notify_all();
	}
private:
	void DispatchThread()
	{
		try
		{
			std::shared_ptr<volatile bool> destroyed = m_destroyed;
			for (;;)
			{
				std::queue<DispatchEntry> disp;
				{
					boost::unique_lock<boost::mutex> lock(m_mutex);
					if (m_dispatches.empty())
						m_cond.wait(lock);
					m_dispatches.swap(disp);
				}
				while (!disp.empty())
				{
					const DispatchEntry& entry = disp.front();
					for each(const Subscriber& s in entry.subscribers)
					{
						std::shared_ptr<IEventQueue> shared = s.lock();
						if (shared)
							shared->PushEvent(entry.evt);
						if (*destroyed == true)
						{
							//printf("EventDispatcher destroyed on same thread\n");
							return;
						}
					}
					disp.pop();
				}
			}
		}
		catch (const boost::thread_interrupted&)
		{
			//printf("evt man interrupted\n");
		}
	}

	std::queue<DispatchEntry> m_dispatches;
	boost::mutex m_mutex;
	boost::condition_variable m_cond;
	boost::thread m_dispatchThread;
	std::shared_ptr<volatile bool> m_destroyed;
};

void AddEventDispatcherFactory(IObjectManager* objMan)
{

	IfaceIdList emits;
	emits.push_back(ID_OF(IEventDispatcher));
	objMan->AddFactory([]{return std::make_shared<EventDispatcher>(); }, "EventDispatcher"
		, IfaceIdList(), emits, true);
}
